﻿/*
Copyright (C) 2009  Tomasz Chrzanowski

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
using Com.Tom.CdcCdcCommon;


namespace Com.TomCdc.ChangeDataCapture
{
    public partial class MainForm : Form
    {
        /// <summary>
        /// Specifies the statuses of the appliaction UI.
        /// </summary>
        private enum MainStatus
        {
            /// <summary>
            /// Last command execution succeeded.
            /// </summary>
            Ready,

            /// <summary>
            /// Last command execution failed.
            /// </summary>
            Error,

            /// <summary>
            /// Last command execution succeeded but the returned data might not be as user was expecting.
            /// </summary>
            Warning,

            /// <summary>
            /// The proccessing is taking place now.
            /// </summary>
            Processing
        }

        /// <summary>
        /// Represents sql connection string to the selector database.
        /// </summary>
        private SqlConnectionStringBuilder _selectorDbConnStr;

        /// <summary>
        /// Represents sql connection string to the store database.
        /// </summary>
        private SqlConnectionStringBuilder _storeDbConnStr;

        /// <summary>
        /// Represents <see cref="SourceRecordHistoryProvider"/> object used to retrieve the change history from 
        /// the database.
        /// </summary>
        private SourceRecordHistoryProvider _srcRecHistoryProvider;

        /// <summary>
        /// Represents <see cref="ViewBuilder"/> object used to populate grid view with the change history.
        /// </summary>
        private ViewBuilder _viewBuilder;

        /// <summary>
        /// Represents collection of <see cref="SourceRecord"/> objects which can be iterated forward and backward.
        /// </summary>
        private ISelector<SourceRecord> _srcRecordSelector;

        /// <summary>
        /// 
        /// </summary>
        private AdvancedSourceRecordIdProvider _advSrcRecProvider;
        private ToolStripPictureBox _statusPicture;
        private BackgroundWorker _bkgWorker;
        private string _statusExtraInfo;

        private const string ICON_STATUS_SUCCESS = "StatusSuccess.ico";
        private const string ICON_STATUS_WARNING = "StatusWarning.ico";
        private const string ICON_STATUS_FAILURE = "StatusFailureSmall.ico";


        public MainForm()
        {
            InitializeComponent();
            _statusPicture = new ToolStripPictureBox();

            uxStatusStripCircle.LoadingCircleControl.OuterCircleRadius = 8;
            uxStatusStripCircle.LoadingCircleControl.InnerCircleRadius = 5;
            uxStatusStripCircle.LoadingCircleControl.NumberSpoke = 10;

            SetMainStatusInfo(MainStatus.Ready, "Ready", null);

            EnableDisableUIControls();
            EnableDisableNavigationButtons();
        }

        private void uxConnectStoreDb_Click(object sender, EventArgs e)
        {
            uxDatabaseConnectionsBox.Focus();

            DatabaseConnection dbConnDialog = new DatabaseConnection();

            if (dbConnDialog.ShowDialog() == DialogResult.OK)
            {
                if (CommonHelper.IsDatabaseOfCdcType(dbConnDialog.TestedConnection.ConnectionString,
                    CdcDatabaseType.Storage))
                {
                    _storeDbConnStr = dbConnDialog.TestedConnection;
                    uxStoreConnInfo.Text = _storeDbConnStr.DataSource + "." + _storeDbConnStr.InitialCatalog;
                    _srcRecHistoryProvider = new SourceRecordHistoryProvider(_storeDbConnStr.ConnectionString);
                    _viewBuilder = new ViewBuilder(_srcRecHistoryProvider, uxChangeHistoryView);
                    LoadTablesList();
                }
                else
                {
                    _storeDbConnStr = null;
                    uxStoreConnInfo.Text = "";
                    _srcRecHistoryProvider = null;
                    _viewBuilder = null;
                    SetMainStatusInfo(MainStatus.Error, "Not storage database", "Selected database has not been " +
                        "recognized as storage database.");
                    MessageBox.Show("Selected database has not been recognized as storage database.", "",
                        MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }

            EnableDisableUIControls();
        }


        private void LoadTablesList()
        {
            if (_storeDbConnStr == null)
            {
                MessageBox.Show("No connection to the store database specified.",
                    "Missing connection", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            EnableDisableAll(false);
            SetMainStatusInfo(MainStatus.Processing, "Loading tables list ...", null);
            _bkgWorker = new BackgroundWorker();
            _bkgWorker.DoWork += new DoWorkEventHandler(LoadTableList_DoWork);
            _bkgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(LoadTableList_RunWorkerCompleted);
            _bkgWorker.RunWorkerAsync();
        }

        void LoadTableList_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            uxTableList.Items.Clear();

            if (e.Error == null)
            {
                uxTableList.Items.AddRange((string[])e.Result);
                SetMainStatusInfo(MainStatus.Ready, "Ready", null);
            }
            else
            {
                SetMainStatusInfo(MainStatus.Error, "Table list loading failed", e.Error.Message);
            }

            EnableDisableAll(true);
        }

        void LoadTableList_DoWork(object sender, DoWorkEventArgs e)
        {
            Dictionary<string, int> result = _srcRecHistoryProvider.GetTables();
            string[] tblList = new string[result.Count];
            result.Keys.CopyTo(tblList, 0);
            e.Result = tblList;
        }

        private bool IsRecordIdAndTableNameCorrect()
        {
            long recordId;
            if (!long.TryParse(uxRecordId.Text, out recordId))
            {
                MessageBox.Show("The record id must be a valid 64 bit signed integer.", "Incorrect value",
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }

            if (uxTableList.SelectedIndex < 0)
            {
                MessageBox.Show("The table name must be selected.", "Missing data.",
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            return true;
        }

        private void uxGetHistorySimple_Click(object sender, EventArgs e)
        {
            uxDatabaseConnectionsBox.Focus();
            GetHistorySimple();
        }

        private void GetHistorySimple()
        {
            if (IsRecordIdAndTableNameCorrect())
            {            
                // prepares source record object and executes the history view generator
                _srcRecordSelector = new Selector<SourceRecord>(
                    new SourceRecord[] { new SourceRecord((string)uxTableList.SelectedItem, long.Parse(uxRecordId.Text)) }
                    );

                DisplayRecordHistory(_srcRecordSelector.GetNext());
            }
        }

        private void uxPreviousEntity_Click(object sender, EventArgs e)
        {
            uxChangeHistoryViewBox.Focus();
            DisplayRecordHistory(_srcRecordSelector.GetPrevious());
        }

        private void uxNextEntity_Click(object sender, EventArgs e)
        {
            uxChangeHistoryViewBox.Focus();
            DisplayRecordHistory(_srcRecordSelector.GetNext());
        }

        private void uxRefreshEntity_Click(object sender, EventArgs e)
        {
            uxChangeHistoryViewBox.Focus();
            DisplayRecordHistory(_srcRecordSelector.GetCurrent());
        }

        private void DisplayRecordHistory(SourceRecord srcRecord)
        {
            EnableDisableAll(false);
            SetMainStatusInfo(MainStatus.Processing, "Change history data loading ...", null);

            _bkgWorker = new BackgroundWorker();
            _bkgWorker.DoWork += new DoWorkEventHandler(DisplayRecordHistory_DoWork);
            _bkgWorker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(DisplayRecordHistory_RunWorkerCompleted);

            _bkgWorker.RunWorkerAsync(srcRecord);
        }

        void DisplayRecordHistory_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            uxChangeHistoryView.Rows.Clear();

            if (e.Error == null)
            {
                object[] result = (object[])e.Result;
                SourceRecord srcRecord = (SourceRecord)result[0];
                DataTable srcRecordHistory = (DataTable)result[1];
                if (srcRecordHistory.Rows.Count > 0)
                {
                    _viewBuilder.PopulateGridView(srcRecord, srcRecordHistory);
                    SetMainStatusInfo(MainStatus.Ready, "Ready", null);
                    uxStatusStripRecordInfo.Text = srcRecord.SourceTableName + "(" + 
                        srcRecord.SourceRowId.ToString() + ")";
                }
                else
                {
                    SetMainStatusInfo(MainStatus.Warning, "No change history available",
                        "No change history available for record id: " + srcRecord.SourceRowId.ToString() +
                        " in the table: " + srcRecord.SourceTableName);
                    uxStatusStripRecordInfo.Text = "";
                }
            }
            else
            {
                uxStatusStripRecordInfo.Text = "";
                SetMainStatusInfo(MainStatus.Error, "Change history data loading failed", e.Error.Message);
            }

            EnableDisableAll(true);
            EnableDisableNavigationButtons();
        }

        void DisplayRecordHistory_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = new object[]{
                e.Argument,
                _srcRecHistoryProvider.GetSrcRecordHistory((SourceRecord)e.Argument)};
        }

        private void uxConnectSelectorDb_Click(object sender, EventArgs e)
        {
            uxDatabaseConnectionsBox.Focus();
            DatabaseConnection dbConnDialog = new DatabaseConnection();

            if (dbConnDialog.ShowDialog() == DialogResult.OK)
            {
                _selectorDbConnStr = dbConnDialog.TestedConnection;
                uxSelectorConnInfo.Text = _selectorDbConnStr.DataSource + "." + _selectorDbConnStr.InitialCatalog;
                _advSrcRecProvider = new AdvancedSourceRecordIdProvider(_selectorDbConnStr.ConnectionString);
                uxSpSelectorsList.Items.Clear();
                uxAdvSelectorParameters.Rows.Clear();
                LoadSelectorsList();
            }

            EnableDisableUIControls();
        }

        private void LoadSelectorsList()
        {
            if (_selectorDbConnStr == null)
            {
                MessageBox.Show("No connection to the tracked database specified.",
                    "Missing connection", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            EnableDisableAll(false);
            SetMainStatusInfo(MainStatus.Processing, "Loading selectros list ...", null);
            _bkgWorker = new BackgroundWorker();
            _bkgWorker.DoWork += new DoWorkEventHandler(LoadSelectorsList_DoWork);
            _bkgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(LoadSelectorsList_RunWorkerCompleted);
            _bkgWorker.RunWorkerAsync();
        }

        void LoadSelectorsList_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            uxSpSelectorsList.Items.Clear();

            if (e.Error == null)
            {
                uxSpSelectorsList.BeginUpdate();
                foreach (SelectorProcedure selProc in (SelectorProcedure[])e.Result)
                {
                    uxSpSelectorsList.Items.Add(selProc);
                }
                uxSpSelectorsList.EndUpdate();

                SetMainStatusInfo(MainStatus.Ready, "Ready", null);
            }
            else
            {
                SetMainStatusInfo(MainStatus.Error, "Selectors list loading failed", e.Error.Message);
            }

            EnableDisableAll(true);
        }

        void LoadSelectorsList_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = _advSrcRecProvider.GetSelectorsList();
        }

        private void uxSpSelectorsList_SelectedIndexChanged(object sender, EventArgs e)
        {
            _advSrcRecProvider.PopulateGridParametersList(uxAdvSelectorParameters, uxSpSelectorsList.SelectedItem);
            EnableDisableUIControls();
        }

        private void uxGetHistoryAdvanced_Click(object sender, EventArgs e)
        {
            uxDatabaseConnectionsBox.Focus();

            if (_storeDbConnStr == null)
            {
                MessageBox.Show("No connection to the store database specified.",
                    "Missing connection", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            if (uxSpSelectorsList.SelectedIndex == -1)
            {
                MessageBox.Show("Selector must be selected from the list and arguments specified.",
                    "Missing data.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            try
            {
                _srcRecordSelector =
                    _advSrcRecProvider.GetSourceRecordsId(uxAdvSelectorParameters, uxSpSelectorsList.SelectedItem);
            }
            catch (CdcViewerException ex)
            {
                MessageBox.Show(ex.Message, "Incorrect data.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Problem occured when retrieving history of changes." + Environment.NewLine +
                    "Details:" + Environment.NewLine + ex.Message, "Incorrect data.",
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            if (_srcRecordSelector.Count > 0)
            {
                DisplayRecordHistory(_srcRecordSelector.GetNext());
            }
            else
            {
                uxChangeHistoryView.Rows.Clear();
                EnableDisableNavigationButtons();
                SetMainStatusInfo(MainStatus.Warning, "Advanced selector returned no data", 
                    "The selector returned no source record ID(s) for provided argument(s).");
                uxStatusStripRecordInfo.Text = "";
            }
        }

        /// <summary>
        /// Sets availability of UI controls, according to current state of colntrols data.
        /// </summary>
        private void EnableDisableUIControls()
        {
            if (_storeDbConnStr == null)
            {
                uxSimpleSelectorBox.Enabled = false;
                uxAdvancedSelectorBox.Enabled = false;
                uxChangeHistoryViewBox.Enabled = false;
                uxTableList.SelectedIndex = -1;
                uxChangeHistoryView.Rows.Clear();
            }
            else
            {
                uxSimpleSelectorBox.Enabled = true;
                uxAdvancedSelectorBox.Enabled = true;
            }

            if (uxTableList.SelectedIndex == -1)
            {
                uxRecordId.Enabled = false;
                uxGetHistorySimple.Enabled = false;
                uxRecordId.Text = "";
            }
            else
            {
                uxRecordId.Enabled = true;
                uxGetHistorySimple.Enabled = true;
            }

            if (_selectorDbConnStr == null)
            {
                uxSpSelectorsList.Enabled = false;
                uxAdvSelectorParameters.Enabled = false;
                uxGetHistoryAdvanced.Enabled = false;
            }
            else
            {
                uxSpSelectorsList.Enabled = true;
            }

            if (uxSpSelectorsList.SelectedIndex == -1)
            {
                uxAdvSelectorParameters.Enabled = false;
                uxGetHistoryAdvanced.Enabled = false;
            }
            else
            {
                uxAdvSelectorParameters.Enabled = true;
                uxGetHistoryAdvanced.Enabled = true;
            }
        }

        /// <summary>
        /// Sets availability of navigation buttons (go next, go previous), depending on the current position
        /// of the item in a <see cref="Selector"/> object.
        /// </summary>
        private void EnableDisableNavigationButtons()
        {
            if (_srcRecordSelector == null || _srcRecordSelector.Count == 0)
            {
                uxPreviousEntity.Enabled = false;
                uxNextEntity.Enabled = false;
                uxRefreshEntity.Enabled = false;
                return;
            }

            uxRefreshEntity.Enabled = true;
            uxChangeHistoryViewBox.Enabled = true;

            if (_srcRecordSelector.IsNext())
            {
                uxNextEntity.Enabled = true;
            }
            else
            {
                uxNextEntity.Enabled = false;
            }

            if (_srcRecordSelector.IsPrevious())
            {
                uxPreviousEntity.Enabled = true;
            }
            else
            {
                uxPreviousEntity.Enabled = false;
            }
        }

        private void EnableDisableAll(bool enable)
        {
            if (enable)
            {
                uxDatabaseConnectionsBox.Enabled = true;
                EnableDisableUIControls();
            }
            else
            {
                uxDatabaseConnectionsBox.Enabled = false;
                uxSimpleSelectorBox.Enabled = false;
                uxAdvancedSelectorBox.Enabled = false;
                uxChangeHistoryViewBox.Enabled = false;
            }
        }

        private void uxTableList_SelectedIndexChanged(object sender, EventArgs e)
        {
            EnableDisableUIControls();
        }

        /// <summary>
        /// Sets current application status indicators.
        /// </summary>
        /// <param name="status">The application status.</param>
        /// <param name="statusText">The information displayed int the status bar.</param>
        /// <param name="extraInfo">Information displayed in the message box when status info link clicked.</param>
        private void SetMainStatusInfo(MainStatus status, string statusText, string extraInfo)
        {
            uxStatusStripInfo.Text = statusText;
            
            if (string.IsNullOrEmpty(extraInfo))
            {
                uxStatusStripInfo.IsLink = false;
            }
            else
            {
                _statusExtraInfo = extraInfo;
                uxStatusStripInfo.IsLink = true;
            }

            switch (status)
            {
                case MainStatus.Processing:
                    uxStatusStrip.Items.RemoveAt(0);
                    uxStatusStrip.Items.Insert(0, uxStatusStripCircle);
                    uxStatusStripCircle.LoadingCircleControl.Enabled = true;
                    uxStatusStripCircle.LoadingCircleControl.Active = true;
                    break;
                case MainStatus.Ready:
                    _statusPicture.PictureBoxControlImage = uxImageList16.Images[ICON_STATUS_SUCCESS];
                    uxStatusStrip.Items.RemoveAt(0);
                    uxStatusStrip.Items.Insert(0, _statusPicture);
                    break;
                case MainStatus.Warning:
                    _statusPicture.PictureBoxControlImage = uxImageList16.Images[ICON_STATUS_WARNING];
                    uxStatusStrip.Items.RemoveAt(0);
                    uxStatusStrip.Items.Insert(0, _statusPicture);
                    break;
                case MainStatus.Error:
                    _statusPicture.PictureBoxControlImage = uxImageList16.Images[ICON_STATUS_FAILURE];
                    uxStatusStrip.Items.RemoveAt(0);
                    uxStatusStrip.Items.Insert(0, _statusPicture);
                    break;
            }
        }

        private void uxStatusStripInfo_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(_statusExtraInfo))
            {
                MessageBox.Show(_statusExtraInfo, "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void uxRecordId_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                GetHistorySimple();
            }
        }

        private void uxChangeHistoryView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            if ((ModifierKeys & Keys.Control) != 0)
            {
                uxChangeHistoryView.Columns[e.ColumnIndex].Frozen = !uxChangeHistoryView.Columns[e.ColumnIndex].Frozen;
            }
         }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SplashScreen splash = new SplashScreen(SplashType.Viewer, int.MaxValue);
            splash.Show();
        }


    }
}

